package com.lookme.lmtool.utils;

import cn.hutool.core.io.IoUtil;
import com.lookme.lmtool.model.ProjectInfo;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.basic.DateConverter;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.*;
import org.apache.commons.lang3.StringUtils;
import org.xmlpull.v1.XmlPullParserException;

import java.io.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.List;
import java.util.TimeZone;

/**
 * 对象系列化工具类
 * @author blake
 */
public class SerializationUtils {
    private static String PREFIX_CDATA = "<![CDATA[";
    private static String SUFFIX_CDATA = "]]>";
        /**
         *
         */
        private static final long serialVersionUID = 3068282609202318740L;

        public static byte[] objToBytes(Serializable obj) {
            return org.apache.commons.lang3.SerializationUtils.serialize(obj);
        }

        public static Serializable bytesToObj(byte[] bytes) {
            return ((Serializable) org.apache.commons.lang3.SerializationUtils.deserialize(bytes));
        }

        public static String objToString(Serializable obj) {
            if (obj == null) {
                return null;
            }
            String ret = null;

            String xml = objToXML(obj);
            Base64.Encoder encoder = Base64.getEncoder();
            ret = encoder.encodeToString(objToBytes(xml));
            return ret;
        }

        public static Serializable stringToObj(String str) throws IOException {
            if (str == null) {
                return null;
            }
            Base64.Decoder decoder = Base64.getDecoder();
            String xml = (String) bytesToObj(decoder.decode(str));
            return ((Serializable) xmlToObj(xml));
        }

        public static void saveToFile(Serializable obj, String fileName) throws IOException {
            InputStream is = new ByteArrayInputStream(objToBytes(obj));
            FileOutputStream fos = new FileOutputStream(fileName);
            byte[] buf = new byte[10240];
            int len = -1;
            while ((len = is.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fos.close();
        }

        public static Serializable readFromFile(String fileName) throws IOException,
                ClassNotFoundException {
            FileInputStream fis = new FileInputStream(fileName);
            ObjectInputStream is = new ObjectInputStream(fis);

            return ((Serializable) is.readObject());
        }

        public static void saveToFileAsXml(Object obj, String fileName) throws IOException {
            String xml = objToXML(obj);
            FileWriter fileWriter = new FileWriter(fileName);
            fileWriter.write(xml);
            fileWriter.close();
        }

        public static Object readFromXmlFile(String fileName) throws IOException, ClassNotFoundException {
            try {
                StringBuffer sBuffer = new StringBuffer();
                FileReader fReader = new FileReader(fileName);
                BufferedReader br = new BufferedReader(fReader);
                String data = null;
                while ((data = br.readLine()) != null) {
                    sBuffer.append(data);
                }

                return xmlToObj(sBuffer.toString());
            }
            catch(Exception e){
                e.printStackTrace();
            }
            return null;
        }

        public static String objToXML(Object obj) {
            final NameCoder nameCoder = new NoNameCoder();
            XStream xStream = createXstream();
            xStream.autodetectAnnotations(true);
            xStream.registerConverter(new DateConverter("yyyy-MM-dd HH:mm:ss", null,TimeZone.getTimeZone("GMT+8")));

            xStream.ignoreUnknownElements();
            return xStream.toXML(obj);
        }

    public static XStream createXstream() {
        return new XStream(new XppDriver() {
            @Override
            public HierarchicalStreamReader createReader(Reader in) {
                try {
                    return new XppReader(in,XppDriver.createDefaultParser(),getNameCoder()){
                        @Override
                        public String getValue() {
                            String data=super.getValue();
                            data=data.replace("【回车】","\n");
                            return data;
                        }
                    };

                } catch (XmlPullParserException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                return new PrettyPrintWriter(out) {
//                    boolean cdata = false;
//                    Class<?> targetClass = null;
//                    @Override
//                    public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
//                        super.startNode(name, clazz);
//                    }

                    @Override
                    protected void writeText(QuickWriter writer, String text) {
//                        if (cdata) {
                            writer.write(PREFIX_CDATA);
                            writer.write(text.replaceAll("\n","【回车】"));
                            writer.write(SUFFIX_CDATA);
//                        } else {
//                            writer.write(text);
//                        }
                    }
                };
            }
        });
    }


        public static Object xmlToObj(String xml) {
            if(StringUtils.isEmpty(xml)){
                return null;
            }
            final NameCoder nameCoder = new NoNameCoder();
            XStream xStream = createXstream();
            XStream.setupDefaultSecurity(xStream); // to be removed after 1.5
            xStream.allowTypesByWildcard(new String[] {
                    "com.lookme.lmtool.**"
            });
            xStream.alias("xml", ProjectInfo.class);
            xStream.autodetectAnnotations(true);
            xStream.registerConverter(new DateConverter("yyyy-MM-dd HH:mm:ss", null,TimeZone.getTimeZone("GMT+8")));
            xStream.ignoreUnknownElements();
            try {
                return xStream.fromXML(xml);
            }catch(Exception e){
                e.printStackTrace();
                return null;
            }
        }

    @SuppressWarnings("unchecked")
    public static <T> List<T> deepCopyList(List<T> src)
    {
        List<T> dest = null;
        try
        {
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(src);
            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream in = new ObjectInputStream(byteIn);
            dest = (List<T>) in.readObject();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return dest;
    }

    public static Object readFromXmlStream(InputStream stream) {
        try {
            StringBuffer sBuffer = new StringBuffer();
            BufferedReader br =IoUtil.getReader(stream,"utf-8");
            String data = null;
            while ((data = br.readLine()) != null) {
                sBuffer.append(data);
            }

            return xmlToObj(sBuffer.toString());
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
